package com.webex.ta.hydra.core.macro;

import com.webex.ta.hydra.core.*;
import com.webex.ta.hydra.core.engine.CommandCompiler;
import com.webex.ta.hydra.core.engine.DefaultHydraEngine;
import com.webex.ta.hydra.core.engine.HydraContextService;
import com.webex.ta.hydra.core.engine.HydraThread;
import com.webex.ta.hydra.util.HydraUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

/**
 * Created by Cisco WebEx.
 * User: vegaz
 * Date: 2010-9-10
 * Time: 16:53:34
 */
public class Macro extends AbstractAction {
    private String macroName;

    public Macro(String macroName) {
        this.macroName = macroName;
    }

    public void execute(TestResult result, String... args) throws Exception {
        MacroDefinition def = MacroRegistry.getInstance().get(macroName);
        if (def == null) {
            // invalid macro
            throw new Exception("Not a valid macro, please make sure a valid keyword name is entered");
        }

        Command rootControl = def.getRoot();
        Vector<ParamDescriptor> descriptors = def.getFields();
        Map<String, String> entries = new HashMap<String, String>();
        for (int i = 0; i < descriptors.size(); i++) {
            if (i < args.length)
                entries.put(descriptors.get(i).getName(), args[i]);
            else
                entries.put(descriptors.get(i).getName(), "");
        }
        // replace the macro parameter found in any of the command parameters.
        MacroParameterReplacer.replaceParameter(rootControl, entries);

        // then compile the commands
        CommandCompiler.compile(rootControl);
        Controller root = (Controller)rootControl.getRuntime();

        Command nextCmd;
        while ((nextCmd = root.next()) != null) {
            if (Thread.currentThread().isInterrupted()) {
                return;
            }

            Keyword kw = nextCmd.getRuntime();
            if (kw != null) {
                if (kw instanceof Action) {
                    doAction(nextCmd);
                } else if (kw instanceof Processor) {
                    doProcessor(nextCmd);
                } else if (kw instanceof Assertion) {
                    doAssertion(nextCmd);
                }
            }
        }

    }


    private void doAction(Command cmd) {
        Command current = HydraContextService.getContext().getCurrentCommand();
        TestResult result = TestResult.createInstance(current);
        Action action = (Action) cmd.getRuntime();
        // generate string arguments from HydraParameter for later usage
        String[] args = HydraThread.getRealValue(action.getParameters());
        result.setKeyword(cmd.getKeyword());
        result.setArguments(args);

        try {
            action.execute(result, args);
        } catch (Exception e) {
            result.setError(true);
            result.setResponse(HydraUtils.printStackTraceToString(e));
        } finally {
            ((DefaultHydraEngine)HydraContextService.getContext().getEngine()).notifyActionListenerOfOccured(result);
        }
        HydraContextService.getContext().setPreviousResult(result);
    }


    private void doAssertion(Command cmd) {
        Assertion assertion = (Assertion) cmd.getRuntime();
        String[] args = HydraThread.getRealValue(assertion.getParameters());
        AssertionResult ar = new AssertionResult();
        ar.setKeyword(cmd.getKeyword());
        ar.setArguments(args);

        TestResult tr = HydraContextService.getContext().getPreviousResult();
        if (tr!= null) {
            assertion.doAssertion(tr, ar, args);
            ar.setParent(tr);
            tr.addAssertionResult(ar);
            if (ar.isError() || ar.isFailure())
                tr.setFailure(true);
            ((DefaultHydraEngine)HydraContextService.getContext().getEngine()).notifyAssertionListenerOfOccured(ar);
        }
    }

    private void doProcessor(Command cmd) {
        Processor processor = (Processor) cmd.getRuntime();

        HydraParameter[] hps = processor.getParameters();
        String[] strArgs = new String[hps.length];
        for (int i = 0; i < hps.length; i++) {
            strArgs[i] = hps[i].getStringValue();
        }

        processor.process(strArgs);
    }

    public ParamDescriptor[] getParamDescriptors(Command cmd) {
        MacroDefinition def =KeywordRegistry.getInstance().getMacroDefinition(macroName);
        if (def == null)
            return new ParamDescriptor[0];
        else {
            return def.getFields().toArray(new ParamDescriptor[def.getFields().size()]);
        }
    }


}
